﻿using System;

using System.Collections.Generic;
using System.Text;
using System.Drawing;
using Silvermoon.Shapes;
using Silvermoon.OpenGL;
using Silvermoon.OpenGL.Native;
using Silvermoon.Core;

using Silvermoon.Interfaces;
using System.Diagnostics;
using Silvermoon.Controls;

namespace Silvermoon.Shapes
{
    /// <summary>
    ///  Abstract <see cref="Shape"/> class.
    ///  A shape represents a rectangle vertex with various textures and colors assigned.
    /// </summary>
    public abstract class Shape : IDisposable, IColor, ICloneable
    {
        [Flags]
        enum ShapeFlags : short
        {
            Visible = 1,
            Disposed = 2
        }
        #region ctor/dtor

        public Shape()
            : base()
        {
#if DEBUG
            UsedShapes++;
          //  Debug.WriteLine("Create: Active Shapes: " + UsedShapes.ToString());
#else
            UsedShapes++;
#endif

            IsInvalidated = false;
            Visible = true;
        }

        public virtual void Dispose()
        {
#if DEBUG
            if (!Disposed)
            {
                UsedShapes--;
                //Debug.WriteLine("Dispose: Active Shapes: " + UsedShapes.ToString());
            }
            else
            {
                //Debug.WriteLine("Redispose: Active Shapes: " + shapeInstanceCounter.ToString());
            }
#else
            if (!Disposed) UsedShapes--;
#endif
            flags |= ShapeFlags.Disposed;
            Transformations = null;
            GC.SuppressFinalize(this);
        }

        ~Shape()
        {
            Dispose();
        }

        #endregion
        #region Fields

        /// <summary>
        /// Gets the number of shapes which are currently allocated.
        /// </summary>
        public static int UsedShapes { get; private set; }

        private ShapeFlags flags;
        private Point location;
        private Size size;
        private int opacity = 255;

        #endregion
        #region Properties

        /// <summary>
        /// Gets or sets whether the control is visible and therefore being rendered.
        /// </summary>
        public bool Disposed
        {
            get { return (flags & ShapeFlags.Disposed) != 0; }
        }

        /// <summary>
        /// Gets or sets whether the shape is visible and therefore being rendered.
        /// </summary>
        public bool Visible
        {
            get { return (flags & ShapeFlags.Visible) != 0; }
            set
            {
                flags = value ? flags | ShapeFlags.Visible : flags & ~ShapeFlags.Visible;
            }
        }

        /// <summary>
        /// Gets or sets the color of the shape if the shape supports color.
        /// </summary>
        public virtual Color Color
        {
            get { return Color.Empty; }
            set { }
        }


        /// <summary>
        /// Gets or sets the coordinates of the upper-left corner of the shape relative to the upper-left corner of its container control.
        /// </summary>
        public Point Location
        {
            get { return location; }
            set { location = value; }
        }

        /// <summary>
        /// Gets or sets the size and location of the shape including its nonclient elements, in pixels, relative to the parent control.
        /// </summary>
        public Rectangle Bounds
        {
            get { Size size = Size; return new Rectangle(location.X, location.Y, size.Width, size.Height); }
            set
            {
                location = new Point(value.X, value.Y);
                Size = new Size(value.Width, value.Height);
            }

        }

        /// <summary>
        /// Used for <see cref="T:ShapeCollection"/> to implement linked lists.
        /// </summary>
        internal Shape NextShape { get; set; }

        /// <summary>
        /// Gets the X coordinate of the shape relative to the parent control.
        /// </summary>
        public int X { get { return location.X; } }


        /// <summary>
        /// Gets the Y coordinate of the shape relative to the parent control.
        /// </summary>
        public int Y { get { return location.Y; } }


        /// <summary>
        /// Gets the width of the shape.
        /// </summary>
        public int Width { get { return Size.Width; } }

        /// <summary>
        /// Gets the height of the shape.
        /// </summary>
        public int Height { get { return Size.Height; } }

        /// <summary>
        /// Gets or sets the opacity of the shape within a range of 0 and 255.
        /// </summary>
        public int Opacity
        {
            get { return opacity; }
            set
            {
                if (opacity < 0) opacity = 0;
                else if (opacity > 255) opacity = 255;
                if (opacity != value)
                {
                    opacity = value;
                    //Invalidate();
                }
            }
        }

        /// <summary>
        /// Gets or sets the size of the shape.
        /// </summary>
        public virtual Size Size
        {
            get { return size; }
            set
            {
                if (size != value)
                {
                    size = value;
                    Invalidate();
                }
            }
        }

        /// <summary>
        /// Gets whether the shape is invalidated.
        /// </summary>
        public bool IsInvalidated { get; protected set; }

        public IEnumerable<Transformation> Transformations { get; set; }

        #endregion
        #region Methods

        /// <summary>
        /// Occurs when the shape needs to layout itself.
        /// </summary>
        protected virtual void Layout()
        {
        }

        /// <summary>
        /// Occurs when the shape should prepare to load.
        /// </summary>
        public void PreLoad()
        {
            if (IsInvalidated)
            {
                Layout();
                IsInvalidated = false;
            }
        }

        /// <summary>
        /// Invalidates the shape.
        /// </summary>
        public void Invalidate()
        {
            this.IsInvalidated = true;
        }

        #endregion
        #region shape

        protected virtual Color GetShapeColor()
        {
            //return Color.Empty;
            return Color;
        }

        /// <summary>
        /// Gets the color to use for drawing a shape.
        /// </summary>
        /// <value></value>
        internal Color ShapeColor
        {
            get { return GetShapeColor(); }
        }

        /// <summary>
        /// Renders the shape.
        /// </summary>
        /// <param name="renderer">Renderer which renders the shape.</param>
        public abstract void Render(Renderer renderer);

        #endregion
        #region operators

        public static Shape FromObject(object obj)
        {
            if (obj is string)
            {
                return new StringShape { Text = (string)obj, AutoSize = true, Alignment = Alignment.Near };
            }
            else if (obj is Texture)
            {
                Texture texture = (Texture)obj;
                return new ImageShape { Image = texture, Size = texture.Size, UseTextColor = true, Color = Color.Empty };
            }
            else if (obj is Color)
            {
                return new RectangleShape { Color = (Color)obj };
            }
            else return null;
        }

        public static implicit operator Shape(Color color)
        {
            if (color.IsEmpty) return Shape.Empty;
            return new RectangleShape { Color = color };
        }

        public static implicit operator Shape(Texture texture)
        {
            return new ImageShape { Image = texture, Size = texture.Size, UseTextColor = true, Color = Color.Empty };
            //return new BackgroundShape { Texture = texture, Size = texture.Size, Color = Color.White, Style = RenderStyle.Simple };

        }

        public static implicit operator Shape(string text)
        {
            return new StringShape { Text = text, AutoSize = true, Alignment = Alignment.Near };
        }

        public static Shape operator &(Shape a, Shape b)
        {
            if (a == null) return a;
            MultiShape sc = a as MultiShape;
            if (sc != null)
            {
                sc.Shapes.Add(b);
                return sc;
            }
            else
            {
                sc = new MultiShape();
                sc.Shapes.Add(a);
                sc.Shapes.Add(b);
                return sc;
            }
        }

        public static Shape operator +(Shape a, Shape b)
        {
            if (a == null) return a;
            MultiShape sc = a as MultiShape;
            if (sc != null)
            {
                sc.Shapes.Add(b);
                return sc;
            }
            else
            {
                sc = new StackShape();
                sc.Shapes.Add(a);
                sc.Shapes.Add(b);
                return sc;
            }
        }

        public static Shape operator |(Shape a, Shape b)
        {
            if (a == null) return a;
            MultiShape sc = a as MultiShape;
            if (sc != null)
            {
                if (!sc.Shapes.Contains(b)) sc.Shapes.Add(b);
                return sc;
            }
            else
            {
                sc = new MultiShape();
                sc.Shapes.Add(a);
                sc.Shapes.Add(b);
                return sc;
            }
        }

        public static Shape operator -(Shape a, Shape b)
        {
            if (a == b) return null;
            MultiShape sc = a as MultiShape;
            if (sc != null)
            {
                sc.Shapes.Remove(b);
                int count = sc.Shapes.Count;
                if (count == 1) return sc.Shapes[0];
                if (count == 0) return null;
                return sc;
            }
            return a;
        }

        #endregion


        public static bool IsNullOrEmpty(Shape shape)
        {
            return (shape == null) || Shape.Empty.Equals(shape);
        }

        #region ICloneable Members

        #endregion

        #region ICloneable Members

        /// <summary>
        /// Creates a new shape clone.
        /// </summary>
        /// <returns>Shape to be returned as Clone().</returns>
        protected abstract Shape CreateClone();


        /// <summary>
        /// Copies data to another shape.
        /// </summary>
        /// <param name="shape">Shape to copy.</param>
        protected virtual void Copy(Shape shape)
        {
            shape.Color = this.Color;
            shape.Bounds = this.Bounds;
            shape.Opacity = Opacity;
            shape.Visible = Visible;
            shape.Transformations = Transformations;
        }

        /// <summary>
        /// Creates a shape clone with the same properties.
        /// </summary>
        /// <returns></returns>
        object ICloneable.Clone()
        {
            Shape shape = CreateClone();
            Copy(shape);
            return shape;
        }

        /// <summary>
        /// Creates a shape clone with the same properties.
        /// </summary>
        /// <returns></returns>
        public Shape Clone()
        {
            Shape shape = CreateClone();
            Copy(shape);
            return shape;
        }

        /// <summary>
        /// Shape that does not perform any rendering.
        /// This is useful  if you want to replace a default shape of a control by none. For instance you can set this as the Backround of a button.
        /// </summary>
        private class EmptyShape : Shape
        {
            public EmptyShape()
                : base()
            {
                Visible = false;
            }


            public override void Render(Renderer renderer) { }

            protected override Shape CreateClone()
            {
                return new EmptyShape();
            }

            protected override void Copy(Shape shape)
            {
                // do nothing since the clone is itself!
            }
        }

        /// <summary>
        /// Gets a shape that does not render.
        /// This is useful  if you want to replace a default shape of a control by none. For instance you can set this as the Backround of a button.
        /// </summary>
        public static Shape Empty
        {
            get { return new EmptyShape(); }
        }

        #endregion
    }

}
